#ifndef FILE_UTIL_H
#define FILE_UTIL_H

#include "base/macro_def.h"
#include <cstdio>
#include <dirent.h>
#include <sys/mman.h>

class SequenceAcessFile {
  public:
    SequenceAcessFile()
        : file_(NULL) {
    }
    ~SequenceAcessFile() {
      if (file_ != NULL) {
        ::fclose(file_);
      }
    }

    bool Init(const std::string& file);

    int32 Read(char* buf, uint32 size);
    void Skip(uint32 len) {
      ::fseek(file_, len, SEEK_CUR);
    }

  private:
    FILE* file_;

    DISALLOW_COPY_AND_ASSIGN(SequenceAcessFile);
};

class AtomicSequenceFile {
  public:
    AtomicSequenceFile()
        : fd_(-1) {
    }
    ~AtomicSequenceFile() {
      if (fd_ != -1) {
        ::close(fd_);
      }
    }

    bool Init(const std::string& file);
    int32 Read(off_t off, char* buf, uint32 size) const;

  private:
    uint32 fd_;

    DISALLOW_COPY_AND_ASSIGN(AtomicSequenceFile);
};

class WriteableFile {
  public:
    explicit WriteableFile(uint32 once_map_size)
        : fd_(-1), once_map_size_(once_map_size), mem_(NULL), wpos_(NULL), end_(
        NULL), file_pos_(0), last_flush_pos_(0) {
    }
    ~WriteableFile();

    bool Init(const std::string& path);

    int32 Append(const char* buf, uint32 len);
    void Sync();

  private:
    uint32 fd_;
    uint32 once_map_size_;

    u_char* mem_;
    u_char* wpos_;
    u_char* end_;
    u_char* last_flush_pos_;

    uint64 file_pos_;

    bool Mmap();
    bool UMap();

    DISALLOW_COPY_AND_ASSIGN(WriteableFile);
};

class Dir {
  public:
    Dir(const std::string& path)
        : path_(path), dir_(NULL) {
      CHECK(!path_.empty());
    }
    ~Dir();

    bool Init();

    const std::string* NextFile() {
      return Next(DT_REG);
    }
    const std::string* NextDir() {
      return Next(DT_DIR);
    }

  private:
    std::string path_;
    DIR* dir_;

    std::string next_;

    const std::string* Next(uint32 type);

    DISALLOW_COPY_AND_ASSIGN(Dir);
};

inline bool FileExist(const std::string& path) {
  if (path.empty()) return false;

  struct stat st = { 0 };
  int ret = ::stat(path.c_str(), &st);
  return ret == 0;
}

inline bool ReName(const std::string& o, const std::string& n) {
  if (o.empty() || n.empty()) return false;

  return ::rename(o.c_str(), n.c_str()) == 0;
}

inline int32_t FileSize(const std::string& path) {
  if (path.empty()) return -1;

  struct stat st = { 0 };
  int ret = ::stat(path.c_str(), &st);
  if (ret == -1) return -1;

  return st.st_size;
}

inline bool FileEmpty(const std::string& path) {
  return FileSize(path) == 0;
}

inline void DeleteFile(const std::string& path) {
  if (path.empty()) return;
  ::unlink(path.c_str());
}

#endif  // FILE_UTIL_H
